package main

import (
	"fmt"
	"time"
)

func main() {
	trafficLight := NewSimpleTrafficLight(500)

	interval := time.NewTicker(5 * time.Second)

	for {
		select {
		case <-interval.C:
			trafficLight.State.Light()
			trafficLight.State.CarPassingSpeed(trafficLight, 25, "CN1024")
			trafficLight.State.NextLight(trafficLight)
		default:

		}

	}
}

/*
状态模式（State Pattern）也叫作状态机模式（State Machine Pattern）状态模式允许对象的内部状态发生改变时，改变它的行为，
就好像对象看起来修改了它实例化的类，状态模式是一种对象行为型模式。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。
当系统中某个对象存在多个状态，这些状态之间可以进行转换，而且对象在不同状态下行为不相同时可以使用状态模式，
把特定于状态的代码抽象到一组独立的状态类中避免过多的状态条件判断，减少维护成本。
主要由环境类角色、抽象状态角色和具体状态角色，三个角色构成。

Context（环境类）：环境类又称为上下文类，它定义客户端需要的接口，内部维护一个当前状态实例，并负责具体状态的切换。
State（抽象状态）：定义状态下的行为，可以有一个或多个行为。
ConcreteState（具体状态）：每一个具体状态类对应环境的一个具体状态，不同的具体状态类其行为有所不同。

*/

// LightState  State interface
type LightState interface {
	// Light  亮起当前状态的交通灯
	Light()
	//	EnterState 转移到新的状态的时候，调用的方法
	EnterState()
	// NextLight 设置一个状态要转变的状态
	NextLight(light *TrafficLight)
	//CarPassingSpeed 检测车速
	CarPassingSpeed(*TrafficLight, int, string)
}

// TrafficLight context 环境类
type TrafficLight struct {
	State      LightState
	SpeedLimit int
}

func NewSimpleTrafficLight(speedLimit int) *TrafficLight {
	return &TrafficLight{
		SpeedLimit: speedLimit,
		State:      NewRedState(),
	}
}

type DefaultLightState struct {
	StateName string
}

func (*DefaultLightState) CarPassingSpeed(road *TrafficLight, speed int, licensePlate string) {
	if speed > road.SpeedLimit {
		fmt.Printf("Car with license %s was speeding\n", licensePlate)
	}
}

func (state *DefaultLightState) EnterState() {
	fmt.Println("change state to:", state.StateName)
}

func (tl *TrafficLight) TransitionState(newState LightState) {
	tl.State = newState
	tl.State.EnterState()
}

type RedState struct {
	DefaultLightState
}

func NewRedState() *RedState {
	state := &RedState{}
	state.StateName = "RED"
	return state
}

func (rs *RedState) Light() {
	fmt.Println("红灯亮起，不可行驶")
}

func (rs *RedState) CarPassingSpeed(light *TrafficLight, speed int, licensePlate string) {
	// 红灯时不能行驶， 所以这里要重写覆盖 DefaultLightState 里定义的这个方法
	if speed > 0 {
		fmt.Printf("Car with license \"%s\" ran a red light!\n", licensePlate)
	}
}

func (rs *RedState) NextLight(light *TrafficLight) {
	light.TransitionState(NewGreenState())
}

type GreenState struct {
	DefaultLightState
}

func NewGreenState() *GreenState {
	state := &GreenState{}
	state.StateName = "GREEN"
	return state
}

func (gs *GreenState) Light() {
	fmt.Println("绿灯亮起，请通行")
}

func (gs *GreenState) NextLight(light *TrafficLight) {
	light.TransitionState(NewAmberState())
}

type AmberState struct {
	DefaultLightState
}

func NewAmberState() *AmberState {
	state := &AmberState{}
	state.StateName = "AMBER"
	return state
}

func (as *AmberState) Light() {
	fmt.Println("黄灯亮起，请注意")
}

func (as *AmberState) NextLight(light *TrafficLight) {
	light.TransitionState(NewRedState())
}

/*
如果对象需要根据自身当前状态进行不同行为， 同时状态的数量非常多且与状态相关的代码会频繁变更的话， 可使用状态模式。
模式将所有特定于状态的代码抽取到一组独立的类中。 这样一来， 可以在独立于其他状态的情况下添加新状态或修改已有状态， 从而减少维护成本。
如果某个类需要根据成员变量的当前值改变自身行为， 从而需要使用大量的条件语句时， 可使用该模式。
状态模式会将这些条件语句的分支抽取到相应状态类的方法中。 通过业务逻辑内聚，减少客户端类的这部分工作。
当相似状态和基于条件的状态机转换中存在许多重复代码时， 可使用状态模式。
状态模式让你能够生成状态类层次结构， 通过将公用代码抽取到抽象基类中来减少重复。
*/

/*
状态模式在工作流或游戏等类型的软件中广泛使用，
如在OA办公系统中，一个审批的状态有多种，而且审批状态不同时对批文的操作也有所差异。
使用状态模式特别适合用于描述工作流对象（如批文）的状态转换以及不同状态下它所具有的行为。

*/

/*状态模式的使用必然会增加系统中类和对象的个数，导致系统运行开销增大。
状态模式的结构与实现都较为复杂，如果使用不当将导致程序结构和代码的混乱，增加系统设计的难度。
状态模式对“开闭原则”的支持并不太好，增加新的状态类需要修改那些负责状态转换的源代码，
否则无法转换到新增状态；而且修改某个状态类的行为也需修改对应类的源代码。

*/
